﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;

namespace PluginAnalyzer
{
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public class PluginAnalyzer : DiagnosticAnalyzer
    {
        private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor("活字格插件", "属性默认值", "属性'{0}'的默认值为true应该标注DefaultValueAttribute,否则会导致保存再打开后默认值丢失", "默认值", DiagnosticSeverity.Warning, isEnabledByDefault: true, description: "默认值为true应该标注DefaultValueAttribute,否则会导致保存再打开后默认值丢失");
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }

        public override void Initialize(AnalysisContext context)
        {
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
            context.EnableConcurrentExecution();

            // TODO: Consider registering other actions that act on syntax instead of or in addition to symbols
            // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information
            context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);

            context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.PropertyDeclaration);
        }

        private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
        {
            if (context.Node is PropertyDeclarationSyntax propertySyntax)
            {
                var semanticModel = context.SemanticModel;
                // 获取属性的符号信息
                var propertySymbol = semanticModel.GetDeclaredSymbol(propertySyntax);
                var attrs = propertySymbol.GetAttributes();

                foreach (var attr in attrs)
                {
                    foreach (var policy in AttributeCheckPolicies)
                    {
                        if (AttributeCheckPolicy.IsType(attr.AttributeClass, policy.FullName))
                        {
                            try
                            {
                                policy.SemanticModel = semanticModel;
                                var error = policy.Check(attr, propertySymbol);
                                if (!string.IsNullOrEmpty(error))
                                {
                                    ReportError(context, propertySymbol.Locations[0], propertySymbol.Name, attr.AttributeClass.Name, error);
                                }
                            }
                            catch
                            {
                            }
                        }
                    }
                }
            }
        }
        static List<AttributeCheckPolicy> _attributeCheckPolicies;
        static List<AttributeCheckPolicy> AttributeCheckPolicies
        {
            get
            {
                if (_attributeCheckPolicies == null)
                {
                    _attributeCheckPolicies = Assembly.GetExecutingAssembly().GetExportedTypes()
                       .Where(i => typeof(AttributeCheckPolicy).IsAssignableFrom(i) && !i.IsAbstract)
                       .Select(i => Activator.CreateInstance(i)).OfType<AttributeCheckPolicy>()
                       .ToList();
                }
                return _attributeCheckPolicies;
            }
        }
        private static void AnalyzeSymbol(SymbolAnalysisContext context)
        {
            if (!(context.Symbol is INamedTypeSymbol namedTypeSymbol) || namedTypeSymbol.IsAbstract)
            {
                return;
            }
            var baseTypeFullName = "GrapeCity.Forguncy.CellTypes.CellType";
            if (!AttributeCheckPolicy.IsType(namedTypeSymbol, baseTypeFullName))
            {
                return;
            }

            foreach (var member in namedTypeSymbol.GetMembers())
            {
                if (member.Kind == SymbolKind.Property)
                {
                    var propertySymbol = (IPropertySymbol)member;

                    try
                    {

                        var perror = PropertyChecker.CheckProperty(propertySymbol);
                        if (!string.IsNullOrEmpty(perror))
                        {
                            ReportError(context, propertySymbol.Locations[0], propertySymbol.Name, "DefaultValue", perror);
                        }
                    }
                    catch
                    {

                    }
                }
            }
        }

        private static void ReportError(SymbolAnalysisContext context, Location location, string propertyName, string AttrbuteName, string error)
        {
            //var diagnostic = Diagnostic.Create(Rule, location, name);
            var diagnostic = Diagnostic.Create(new DiagnosticDescriptor("活字格插件", AttrbuteName, error, "属性注解", DiagnosticSeverity.Warning, true), location, propertyName);
            context.ReportDiagnostic(diagnostic);
        }
        private static void ReportError(SyntaxNodeAnalysisContext context, Location location, string propertyName, string AttrbuteName, string error)
        {
            //var diagnostic = Diagnostic.Create(Rule, location, name);
            var diagnostic = Diagnostic.Create(new DiagnosticDescriptor("活字格插件", AttrbuteName, error, "属性注解", DiagnosticSeverity.Warning, true), location, propertyName);
            context.ReportDiagnostic(diagnostic);
        }
    }
}